/******************************************************************************
 * This file is part of libemb.
 *
 * libemb is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * libemb is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with libemb.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Project: Embedme
 * Author : FergusZeng
 * Email  : cblock@126.com
 * git	  : https://git.oschina.net/cblock/embedme
 * Copyright 2014-2020 @ ShenZhen ,China
*******************************************************************************/

#ifndef __COROUTINE_H__
#define __COROUTINE_H__

#include "BaseType.h"
#include "Thread.h"
#include <ucontext.h>
#include <map>

/* Linux进程最大堆栈空间为8M-8388608Bytes(可通过"ulimit -s"查看,值单位为K) */
#define $COROUTINE_STACK_SIZE (1048576)	/* 1M bytes:1024*1024=1048576 */
//#define $COROUTINE_STACK_SIZE (8000000)	/* 8M */


namespace libemb{

class CoScheduler;

enum class CoroutineState
{
	Stop=0,
	Start,
	Suspend
};

class Coroutine{
public:
	Coroutine();
	virtual ~Coroutine();
	virtual void routine()=0;
	virtual int coroutineID();
	virtual CoroutineState state();
	virtual void usleep(int us);
	virtual void msleep(int ms);
	virtual bool isRunning();
protected:
	void resume();	
	void yield();
private:
	void saveStack();
	void restoreStack();
private:
	friend class CoScheduler;
	CoScheduler* m_scheduler;
	int m_id;
	ucontext_t m_context;
	CoroutineState m_state{CoroutineState::Stop};
	int m_stackSize{0};
	std::unique_ptr<char[]> m_stackBuffer;
};

class CoScheduler:public Runnable{
public:
	CoScheduler();
	virtual ~CoScheduler();
	bool start(std::shared_ptr<Coroutine> coroutine);
	void schedule(int coroutineID);/* 调度协程 */
	void stop(int coroutineID);/* 删除协程 */
	int routines();/* 查询当前协程个数 */
	void* context();
	char* stack();
private:
	static void coroutineEntry(void* coroutine);
	void run();
private:
	std::map<int, std::shared_ptr<Coroutine>> m_coroutineMap;
	ucontext_t m_context;
	char m_stack[$COROUTINE_STACK_SIZE];
};

}
#endif
